package com.rc.saas.tenant.interceptor;

import com.rc.saas.tenant.common.utils.AddressUtils;
import com.rc.saas.tenant.common.utils.HandlerMethodUtil;
import com.rc.saas.tenant.common.utils.IpUtils;
import com.rc.saas.tenant.model.tenant.TenantUser;
import com.rc.saas.tenant.mq.TenantOperateLogAssemble;
import com.rc.saas.tenant.shiro.ShiroUtils;
import com.rc.saas.tenant.vo.log.TenantOperateLog;
import eu.bitwalker.useragentutils.UserAgent;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * 运营商操作拦截器
 * Created by sven on 2019/11/9
 */
public class TenantOperateLogInterceptor extends HandlerInterceptorAdapter {
    private static Logger logger = LogManager.getLogger(TenantOperateLogInterceptor.class);

    @Autowired
    private TenantOperateLogAssemble tenantOperateLogAssemble;

    /**
     * 该方法将在请求处理之前进行调用
     * preHandle：拦截于请求刚进入时，进行判断，需要boolean返回值，如果返回true将继续执行，如果返回false，将不进行执行。一般用于登录校验
     * 1.当preHandle方法返回false时，从当前拦截器往回执行所有拦截器的afterCompletion方法，再退出拦截器链。也就是说，请求不继续往下传了，直接沿着来的链往回跑。
     * 2.当preHandle方法全为true时，执行下一个拦截器,直到所有拦截器执行完。再运行被拦截的Controller。然后进入拦截器链，运行所有拦截器的postHandle方法,完后从最后一个拦截器往回执行所有拦截器的afterCompletion方法.
     *
     * @param request  请求
     * @param response 响应
     * @param handler  操作
     * @return true继续进入下一个拦截器的preHandle方法或者进入对应的Controller方法，false终止请求
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //如果不是映射到方法直接通过
        //ResourceHttpRequestHandler ["classpath:/static/", "classpath:/view/", "classpath:/public/", "classpath:/resources/", "classpath:/META-INF/resources/", "/"]
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        //return super.preHandle(request, response, handler);

        String url = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getServletPath();
        logger.info("preHandle:{}", url);

        //获取用户IP
        String ip = IpUtils.getIpAddr(request);

        //方法名称
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        logger.info("preHandle.handlerMethod:{}", handlerMethod.getMethod());
        UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));

        //当前登录用户名
        try {
            TenantUser user = ShiroUtils.getSessionUser();
            if (user != null && StringUtils.isNotBlank(user.getLoginName())) {
                TenantOperateLog log = new TenantOperateLog();
                log.setIp(ip);
                String address = AddressUtils.getRealAddressByIP(ip);
                log.setLoginLocation(address);  //登录地址
                log.setUrl(url);    //请求地址全路径
                log.setHandlerMethod(handlerMethod.getMethod().getName());  //接口地址
                log.setTenantCode(user.getTenantCode());
                log.setTenantOperatorCode(user.getTenantOperatorCode());
                log.setUserType(user.getUserType());
                log.setOperator(user.getLoginName());
                log.setOs(userAgent.getOperatingSystem().getName());    //操作系统
                log.setBrowser(userAgent.getBrowser().getName());           //浏览器
                log.setActionType(HandlerMethodUtil.getActionType(handlerMethod.getMethod().getName())); //动作类型
                log.setTableName(HandlerMethodUtil.getTableName(handlerMethod.getMethod().getName(), 0)); //数据表
                log.setTableModule(HandlerMethodUtil.getTableName(handlerMethod.getMethod().getName(), 1)); //数据模块

                Map<String, String> params = new HashMap<String, String>();
                Map requestParams = request.getParameterMap();
                for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
                    String name = (String) iter.next();
                    String[] values = (String[]) requestParams.get(name);
                    String valueStr = "";

                    for (int i = 0; i < values.length; i++) {
                        valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
                    }

                    //乱码解决，这段代码在出现乱码时使用。
                    //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
                    params.put(name, valueStr);
                }
                //map转string
                String passBackParams = mapToString(params);

                log.setRequestParams(passBackParams);
                tenantOperateLogAssemble.assembleTenantOperateLog(log);

                logger.info("postHandle,user:{},handlerMethod:{},ip:{},url:{},params:{}", user.getLoginName(), handlerMethod.getMethod(), ip, url, params);
            }

        } catch (Exception e) {
            logger.error("记录运营商/分销商操作日志异常", e);
        }

        return true;
    }

    /**
     * 在调用Controller方法后，在DispatcherServlet 进行视图返回渲染视图前进行调用
     * @param request      请求
     * @param response     响应
     * @param handler      操作
     * @param modelAndView 视图
     * @throws Exception
     */
    //@Override
    //public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    //    //如果不是映射到方法直接通过
    //    //ResourceHttpRequestHandler ["classpath:/static/", "classpath:/view/", "classpath:/public/", "classpath:/resources/", "classpath:/META-INF/resources/", "/"]
    //    if (!(handler instanceof HandlerMethod)) {
    //        return;
    //    }
    //
    //    String url = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getServletPath();
    //
    //    logger.info("postHandle:{}", url);
    //
    //    //获取用户IP
    //    String ip = getRemoteIp(request);
    //
    //    //方法名称
    //    HandlerMethod handlerMethod = (HandlerMethod) handler;
    //    //logger.info("preHandle,handlerMethod:{}", handlerMethod.getMethod());
    //
    //    //当前登录用户名
    //    try {
    //        TenantUser user = ShiroUtils.getSessionUser();
    //        if (user != null && StringUtils.isNotBlank(user.getLoginName())) {
    //            TenantOperateLog log = new TenantOperateLog();
    //            log.setIp(ip);
    //            log.setUrl(url);
    //            log.setHandlerMethod(handlerMethod.getMethod().getName());
    //            log.setTenantCode(user.getTenantCode());
    //            log.setTenantOperatorCode(user.getTenantOperatorCode());
    //            log.setUserType(user.getUserType());
    //            log.setOperator(user.getLoginName());
    //
    //            Map<String, String> params = new HashMap<String, String>();
    //            Map requestParams = request.getParameterMap();
    //            for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
    //                String name = (String) iter.next();
    //                String[] values = (String[]) requestParams.get(name);
    //                String valueStr = "";
    //
    //                for (int i = 0; i < values.length; i++) {
    //                    valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
    //                }
    //
    //                //乱码解决，这段代码在出现乱码时使用。
    //                //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
    //                params.put(name, valueStr);
    //            }
    //            //map转string
    //            String passBackParams = mapToString(params);
    //
    //            log.setRequestParams(passBackParams);
    //            tenantOperateLogAssemble.assembleTenantOperateLog(log);
    //
    //            logger.info("postHandle,user:{},handlerMethod:{},ip:{},url:{},params:{}", user.getLoginName(), handlerMethod.getMethod(), ip, url, params);
    //            //logger.info("tenant:{},ip:{},user:{}", log.getTenantCode(), log.getIp(), log.getOperator());
    //            //logger.info("parameter:{}", request.getParameterMap());
    //        }
    //
    //    } catch (Exception e) {
    //        logger.error("记录运营商/分销商操作日志异常", e);
    //    }
    //}

    /**
     * 在视图渲染后调用
     *
     * @param request  请求
     * @param response 响应
     * @param handler  操作
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        super.afterCompletion(request, response, handler, ex);
    }

    /**
     * 获取远程网协地址
     *
     * @param request HttpServletRequest
     * @return 远程网协地址
     */
    private String getRemoteIp(HttpServletRequest request) {
        if (request == null) {
            return null;
        }
        String remoteIp = request.getHeader("x-forwarded-for");
        if (StringUtils.isBlank(remoteIp) || StringUtils.equalsIgnoreCase(remoteIp, "unknown")) {
            remoteIp = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(remoteIp) || StringUtils.equalsIgnoreCase(remoteIp, "unknown")) {
            remoteIp = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(remoteIp) || StringUtils.equalsIgnoreCase(remoteIp, "unknown")) {
            remoteIp = request.getHeader("HTTP_CLIENT_IP");
        }
        if (StringUtils.isBlank(remoteIp) || StringUtils.equalsIgnoreCase(remoteIp, "unknown")) {
            remoteIp = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (StringUtils.isBlank(remoteIp) || StringUtils.equalsIgnoreCase(remoteIp, "unknown")) {
            remoteIp = request.getRemoteAddr();
        }
        return remoteIp;
    }

    /**
     * map转str
     *
     * @param map
     * @return
     */
    private String mapToString(Map<String, String> map) {
        Set<String> keySet = map.keySet();
        //将set集合转换为数组
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        //给数组排序(升序)
        Arrays.sort(keyArray);
        //因为String拼接效率会很低的，所以转用StringBuilder。博主会在这篇博文发后不久，会更新一篇String与StringBuilder开发时的抉择的博文。
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < keyArray.length; i++) {
            // 参数值为空，则不参与签名 这个方法trim()是去空格
            if (map.get(keyArray[i]).trim().length() > 0) {
                sb.append(keyArray[i]).append("=").append(map.get(keyArray[i]).trim());
            }
            if (i != keyArray.length - 1) {
                sb.append("&");
            }
        }
        return sb.toString();
    }
}
